package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;

/**
 * 由于用户可能访问未被拦截的接口，导致token不刷新登录失败，这里做一个只要用户有访问接口动作就刷新token
 */
public class RefreshTokenInterceptor implements HandlerInterceptor {


    private StringRedisTemplate stringRedisTemplate;

    /**
     * 为什么要在这创建一个 stringRedisTemplate 构造方法
     * 因为这里的拦截器，是由我们手动创建的对象，无法使用spring注入
     * 但是这个对象是由于Mvc配置类 来创建对象 配置类可以注入stringRedisTemplate 到类中，我们来使用
     * @param stringRedisTemplate
     */
    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 拦截器链 用户首先访问此拦截器，查到的用户信息保存在ThreadLocal 中后续拦截器从中获取用户信息
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取用户传递Token 如果没有token 直接放行后续拦截器处理
        String Token = request.getHeader("authorization");
        if (StrUtil.isBlank(Token)) {
            return true;
        }
        // 当用户有token
        String TokenConfig = LOGIN_USER_KEY + Token;

        // 获得获取用户
        Map<Object, Object> UserMap = stringRedisTemplate.opsForHash().entries(TokenConfig);

        // 判断用户是否存在 如果用户不存在 过期了直接放行 后续拦截器会处理
        if (UserMap.isEmpty()) {
            return true;
        }
        // Map 转化为 UserDto
        /**
         * BeanUtil.fillBeanWithMap
         * 参数 1 数据源
         * 参数 2 数据转换类
         * 参数 3 数据转换过程中报错是否忽略
         */
        UserDTO userDTO = BeanUtil.fillBeanWithMap(UserMap,new UserDTO(), false);

        // 保存在ThreadLocal
        UserHolder.saveUser(userDTO);

        // 刷新用户token时长
        stringRedisTemplate.expire(TokenConfig,LOGIN_USER_TTL,TimeUnit.SECONDS);
        return true;
    }

    /**
     * 在经过controller之后，会删除线程中的用户信息，为啥要删除，当前用户信息是保存在线程中，每一次请求都会携带session并创建一个新线程
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
}
